/**
 * Copyright (c) 2013, AppliedMicro Corp. All rights reserved.
 * Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>
 *
 * This program and the accompanying materials
 * are licensed and made available under the terms and conditions of the BSD License
 * which accompanies this distribution.  The full text of the license may be found at
 * http://opensource.org/licenses/bsd-license.php
 *
 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 *
 **/

#include <PiDxe.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
//#include <Library/NorFlashPlatformLib.h>
#include <Library/PhytiumSpiNorFlashLib.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeLib.h>
#include <Library/MemoryAllocationLib.h>

#include <Guid/GlobalVariable.h>
#include <Guid/EventGroup.h>
#include "PhytiumSpiNorFlashLib.h"
#include <Library/ArmSmcLib.h>
#include <OEMSvc.h>
#include <Protocol/Cpu.h>
#include <Library/CacheMaintenanceLib.h>

typedef struct {
  UINT32 Flash_Index;
  UINT32 Flash_Write;
  UINT32 Flash_Erase;
  UINT32 Flash_Pp;
}FLASH_CMD_INFO;


#define NOR_FLASH_DEVICE_COUNT   1

STATIC EFI_EVENT                 mPlatformNorFlashVirtualAddrChangeEvent;
STATIC UINTN                     mNorFlashControlBase;
STATIC UINT32					 mCmd_Write = 0x2;
STATIC UINT32					 mCmd_Eares = 0xD8;
STATIC UINT32					 mCmd_Pp = 0x6;
STATIC UINT32                    mCmd_Write16= 0x12;

#define SPI_FLASH_BASE           FixedPcdGet64 (PcdSpiFlashBase)
#define SPI_FLASH_SIZE           FixedPcdGet64 (PcdSpiFlashSize)
#define SPI_CONTROLLER_BASE      FixedPcdGet64 (PcdSpiControllerBase)
#define SPI_CONTROLLER_SIZE      FixedPcdGet64 (PcdSpiControllerSize)
#define O_PARAMETER_BASE		 0x400000
#define PM_DEV_INTERFACE_OFFSET	 0x400
#define FLASH_WRITE_CMD_OFFSET					0x10
#define FLASH_ERASER_CMD_OFFSET					0x14
#define FLASH_PP_CMD_OFFSET						0x18
#define CMD_WRITE	0x0
#define CMD_ERASER	0x1
#define CMD_PP		0x2

/*
 * the codes of the different commands
 */
#define CMD_WRDI    0x4
#define CMD_RDID    0x9F
#define CMD_RDSR1   0x5         //Read Status Register-1
#define CMD_WRSR1   0x1         //Write Status Register-1
#define CMD_RDSR2   0x35        //Read Status Register-2
#define CMD_WRSR2   0x31        //Write Status Register-2
#define CMD_RDSR3   0x15        //Read Status Register-3
#define CMD_WRSR3   0x11        //Write Status Register-2
#define CMD_WREN    0x6
#define CMD_ERASE   0xD8
#define CMD_RDAR    0x65
#define CMD_P4E     0x20
#define CMD_BLOCK_LOCK            0x36
#define CMD_BLOCK_UNLOCK          0x39
#define CMD_GLOBAL_BLOCK_UNLOCK   0x98

//[jqmao-001]
#if 0

static inline void cmd_enable(void)
{
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
}


static inline UINT32 SPI_Read_SR(UINT8 SRx)
{
  volatile UINT32 status;

  MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x402000 | (SRx << 24));
  cmd_enable();
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  status = *(volatile UINT32 *)(mNorFlashControlBase + REG_LD_PORT);

  return status;
}

static inline void WaitWip(void)
{
  volatile UINT32  status;

  do {
    status = SPI_Read_SR(CMD_RDSR1);
  } while (status & 0x1);

  return;
}


static inline void WriteEnable(void)
{
  volatile UINT32  status;

  /*
   * test the WEL bit of status register until it becomes 1
   */
  do {
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (CMD_WREN << 24));
    cmd_enable();
    status = SPI_Read_SR(CMD_RDSR1);

  } while (!(status & 0x2));
  // DEBUG((EFI_D_INFO, "%a(%d) status=0x%x\n", __FUNCTION__, __LINE__,status));
}


static inline void WriteDisable(void)
{
  volatile UINT32  status;

  /*
   * test the WEL bit of status register until it becomes 0
   */
  do {
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (CMD_WRDI << 24));
    cmd_enable();
    status = SPI_Read_SR(CMD_RDSR1);

  } while (status & 0x2);
   //DEBUG((EFI_D_INFO, "%a(%d) status=0x%x\n", __FUNCTION__, __LINE__,status));
}


static inline VOID SPI_Write_SR(UINT8 SRx, UINT32 Vlaue)
{

  WriteEnable();

  switch (SRx)
  {
  case CMD_WRSR1:
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x402000 | (CMD_WRSR1 << 24));
    break;

  case CMD_WRSR3:
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x402000 | (CMD_WRSR3 << 24));
    break;

  case CMD_WRSR2:
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x402000 | (CMD_WRSR2 << 24));
    break;

  default:
    break;
  }

  MmioWrite32(mNorFlashControlBase + REG_HD_PORT, 0);
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, Vlaue);
  WaitWip();
}


VOID
IndividualBlockLock(UINT32 BlockLockAdr)
  {
    UINT32          value;
    value = SPI_Read_SR(CMD_RDSR3);
    SPI_Write_SR(CMD_WRSR3,(value | 0x04));

    WriteDisable();
    WriteEnable();
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x408000 | (CMD_BLOCK_LOCK << 24));
    MmioWrite32(mNorFlashControlBase + REG_ADDR_PORT, BlockLockAdr);
    cmd_enable();

  }

VOID
ProtectGD25LB (void)
{

  UINT32          value;
  UINT32          value1;
  UINT32          value2;

 value1 = SPI_Read_SR(CMD_RDSR1);
 DEBUG((EFI_D_INFO, "%a(%d) value1=0x%x\n", __FUNCTION__, __LINE__,value1));

  value2 = SPI_Read_SR(CMD_RDSR2);
  DEBUG((EFI_D_INFO, "%a(%d) value2=0x%x\n", __FUNCTION__, __LINE__,value2));

 value = ((value2 | 0x40 )<< 8) + (value1 | 0x18);
 DEBUG((EFI_D_INFO, "%a(%d) value=0x%x\n", __FUNCTION__, __LINE__,value));

 SPI_Write_SR(CMD_WRSR1,value);

value1 = SPI_Read_SR(CMD_RDSR1);
 DEBUG((EFI_D_INFO, "%a(%d) value1=0x%x\n", __FUNCTION__, __LINE__,value1));

value2 = SPI_Read_SR(CMD_RDSR2);
DEBUG((EFI_D_INFO, "%a(%d) value2=0x%x\n", __FUNCTION__, __LINE__,value2));

}


EFI_STATUS
EFIAPI
ProtectW25Q (
  IN UINTN                  BlockLockAdr,
  IN UINTN                  Length
)
{
  EFI_STATUS          Status;
  UINTN           Index;
  UINTN           Count;


  Status = EFI_SUCCESS;

  if ((Length % SIZE_64KB) == 0)
  {
    Count = Length / SIZE_64KB;
    DEBUG((EFI_D_INFO, "%a() Line=%d,Count=%d\n", __FUNCTION__, __LINE__,Count));

   for(Index = 0; Index < Count; Index ++)
   {

    //DEBUG((EFI_D_INFO, "%a() Line=%d,BlockLockAdr=0x%x\n", __FUNCTION__, __LINE__,BlockLockAdr));
    IndividualBlockLock(BlockLockAdr);
    BlockLockAdr=BlockLockAdr + SIZE_64KB;
   }
   }
   else
   {
    Status = EFI_INVALID_PARAMETER;
   }
    return Status;

}




#endif
//[jqmao-001]





NOR_FLASH_DESCRIPTION_PHYTIUM mNorFlashDevices[NOR_FLASH_DEVICE_COUNT] = {
  {
    SPI_FLASH_BASE,   /* Device Base Address */
    SPI_FLASH_BASE,   /* Region Base Address */
    SIZE_1MB * 16,    /* Size */
    SIZE_64KB,        /* Block Size */
    {0xE7223039, 0x5836, 0x41E1, { 0xB5, 0x42, 0xD7, 0xEC, 0x73, 0x6C, 0x5E, 0x59 } }
  },
};

//EFI_GUID  norSpiFlashGuid = {0xE7223039, 0x5836, 0x41E1, {0xB5, 0x42, 0xD7, 0xEC, 0x73, 0x6C, 0x5E, 0x59}};


#if 1
static inline void flush_flash(UINTN Address, UINT32 Value)
{
  EFI_STATUS Status;
  EFI_CPU_ARCH_PROTOCOL    *gCpu;

  // Get the Cpu protocol for later use
  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
  DEBUG((EFI_D_INFO, "%a() Line=%d\n", __FUNCTION__, __LINE__));
  ASSERT_EFI_ERROR(Status);

  Status = gCpu->FlushDataCache (gCpu, Address, 4, EfiCpuFlushTypeWriteBack);
  DEBUG((EFI_D_INFO, "%a() Line=%d\n", __FUNCTION__, __LINE__));
  ASSERT_EFI_ERROR(Status);
  DEBUG((EFI_D_INFO, "%a() Line=%d\n", __FUNCTION__, __LINE__));
}

EFI_STATUS FlushFlash(UINTN Address, UINT32 BufferSizeInBytes)
{
#if 0
  EFI_STATUS Status;
  EFI_CPU_ARCH_PROTOCOL    *gCpu;

  // Get the Cpu protocol for later use
  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
  if(EFI_ERROR(Status)) {
    DEBUG((EFI_D_INFO, "%a() Line=%d\n", __FUNCTION__, __LINE__));
    return Status;
  }

  Status = gCpu->FlushDataCache (gCpu, Address, BufferSizeInBytes, EfiCpuFlushTypeWriteBack);
  if(EFI_ERROR(Status)) {
    DEBUG((EFI_D_INFO, "%a() Line=%d\n", __FUNCTION__, __LINE__));
    return Status;
  }
#endif

  WriteBackDataCacheRange ((VOID *)(UINTN)Address, (UINTN)BufferSizeInBytes);
  return EFI_SUCCESS;
}

#endif

/*can write 256 bytes one time*/
static inline
EFI_STATUS
SpiWrite(
  IN UINTN            Address,
  IN VOID             *Buffer,
  IN UINT32           BufferSizeInBytes
  )
{
  UINT32 cmd_id = 0;
  UINT32 Index;
  UINT32 *TemBuffer = Buffer;
  EFI_STATUS Status;


  if(BufferSizeInBytes > 256) {
    DEBUG((EFI_D_ERROR, "Max Len is 256\n"));
    return EFI_INVALID_PARAMETER;
  }

  if(BufferSizeInBytes % 4 != 0) {
    DEBUG((EFI_D_ERROR, "Len must aligne 4 bytes\n"));
    return EFI_INVALID_PARAMETER;
  }

  if(Address % 4 != 0) {
    DEBUG((EFI_D_ERROR, "Address not aligne 4 byte \n"));
    return EFI_INVALID_PARAMETER;
  }

  cmd_id = mCmd_Pp;
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  cmd_id = mCmd_Write;
    MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x000208 | (cmd_id << 24));

  for(Index = 0; Index < BufferSizeInBytes/4; Index++) {
    MmioWrite32(Address + Index * 4, TemBuffer[Index]);
  }
  Status = FlushFlash(Address, BufferSizeInBytes);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  MmioWrite32(mNorFlashControlBase + REG_FLUSH_REG, 0x1);

  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x0);

  return Status;
}

/*
static inline void SPI_Write_Word(UINTN Address, UINT32 Value)
{
  UINT32 cmd_id = 0;
  if (Value == 0xffffffff)
    return;

  if(Address % 4 != 0) {
    DEBUG((EFI_D_ERROR, "Address not aligne 4 byte \n"));
  }

  cmd_id = mCmd_Pp;
  MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");
  cmd_id = mCmd_Write;
  MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x000208 | (cmd_id << 24) | 0x10);
  MmioWrite32(Address, Value);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");
  MmioWrite32(mNorFlashControlBase + REG_FLUSH_REG, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");
  MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x0);
  flush_flash(Address, Value);
}
*/
UINT32 ReadDeviceId()
{
  UINT32 Reg = 0;

  MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x9F002040);
  Reg = MmioRead32(mNorFlashControlBase + REG_LD_PORT);
  return Reg;
}


static inline void S25FS128S_Erase_Sector()
{
  UINT32 cmd_id, Index;
  UINTN BlockAddress = 0;

  for (Index = 0; Index < 8; Index++) {
    cmd_id = mCmd_Pp;
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
    MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
    asm volatile ("isb sy":::"cc");
    asm volatile ("dsb sy":::"cc");

    cmd_id = 0x20;
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x408000 |(cmd_id << 24));
    MmioWrite32(mNorFlashControlBase + REG_ADDR_PORT, BlockAddress);
    MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
    asm volatile ("isb sy":::"cc");
    asm volatile ("dsb sy":::"cc");
    BlockAddress += 0x1000;
  }
}

static inline void SPI_Erase_Sector(UINTN BlockAddress)
{
  UINT32 cmd_id;
  UINT32 FlashId;

  //S25FS128S block0 32Kb must special hand
  if (BlockAddress == 0) {
    FlashId = ReadDeviceId();
    if (0x4D182001 == FlashId) {
      S25FS128S_Erase_Sector();
    }
  }

  cmd_id = mCmd_Pp;
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  cmd_id = mCmd_Eares;
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x408000 |(cmd_id << 24));
  MmioWrite32(mNorFlashControlBase + REG_ADDR_PORT, BlockAddress);
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");
}


/**
  Fixup internal data so that EFI can be call in virtual mode.
  Call the passed in Child Notify event and convert any pointers in
  lib to virtual mode.

  @param[in]    Event   The Event that is being processed
  @param[in]    Context Event Context
**/
VOID
EFIAPI
PlatformNorFlashVirtualNotifyEvent (
  IN EFI_EVENT            Event,
  IN VOID                 *Context
  )
{
  EfiConvertPointer (0x0, (VOID **)&mNorFlashControlBase);

  return;
}

EFI_STATUS
NorFlashPlatformInitialization (
  VOID
  )
{
  EFI_STATUS     Status;
  DEBUG((EFI_D_BLKIO, "NorFlashPlatformInitialization\n"));

  mNorFlashControlBase = FixedPcdGet64 (PcdSpiControllerBase);


#if 0
  UINTN  Test;
  UINTN  test2 = 0x123;
  UINTN  *p = &test2;
  UINTN  *Buffer;
  NorFlashPlatformRead(0x1200000,Buffer,1);

  Test      = MmioRead32(0x1400000);
  DEBUG((EFI_D_INFO,"The number 0x1400000 is 0x%lx\n",Test));
  SPI_Erase_Sector(0x1400000);
  Test      = MmioRead32(0x1400000);
  DEBUG((EFI_D_INFO,"The First Erase number 0x1400000 is 0x%lx\n",Test));
  SpiWrite(0x1400000, p, 4);
  Test      = MmioRead32(0x1400000);
  DEBUG((EFI_D_INFO,"The SpiWrite number 0x1400000 is 0x%lx\n\n\n",Test));
  SPI_Erase_Sector(0x1400000);
  Test      = MmioRead32(0x1400000);
  DEBUG((EFI_D_INFO,"The Erase after write 0x1400000 is 0x%lx\n",Test));
#endif


  //
  // Register for the virtual address change event
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  PlatformNorFlashVirtualNotifyEvent,
                  NULL,
                  &gEfiEventVirtualAddressChangeGuid,
                  &mPlatformNorFlashVirtualAddrChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  return Status;
}
//[gliu-0025]add-start
EFI_STATUS
NorFlashPlatformExit (
  VOID
  )
{
  EFI_STATUS     Status;

  DEBUG((EFI_D_BLKIO, "NorFlashPlatformExit\n"));
  //
  // Register for the virtual address change event
  //
  Status = gBS->CloseEvent(mPlatformNorFlashVirtualAddrChangeEvent);
  ASSERT_EFI_ERROR (Status);

  return Status;
}
//[gliu-0025]add-end

EFI_STATUS
NorFlashPlatformGetDevices (
  OUT NOR_FLASH_DESCRIPTION_PHYTIUM   **NorFlashDevices,
  OUT UINT32                  *Count
  )
{

  if ((NorFlashDevices == NULL) || (Count == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  *NorFlashDevices = mNorFlashDevices;
  *Count = NOR_FLASH_DEVICE_COUNT;

  return EFI_SUCCESS;
}

EFI_STATUS
NorFlashPlatformRead (
  IN UINTN            Address,
  IN VOID             *Buffer,
  IN UINT32           BufferSizeInBytes
  )
{

  CopyMem ((VOID *)Buffer, (VOID *)Address, BufferSizeInBytes);

  return EFI_SUCCESS;
}

EFI_STATUS
NorFlashPlatformEraseSingleBlock (
  IN UINTN            BlockAddress
  )
{
  DEBUG((EFI_D_BLKIO, "NorFlashPlatformEraseSingleBlock: BlockAddress: 0x%lx,mNorFlashControlBase:%lx\n", BlockAddress,mNorFlashControlBase));

  SPI_Erase_Sector(BlockAddress);

  return EFI_SUCCESS;
}

EFI_STATUS
NorFlashPlatformWrite (
  IN UINTN            Address,
  IN VOID             *Buffer,
  IN UINT32           BufferSizeInBytes
  )
{
  UINT32 Index = 0, Remainder = 0, Quotient = 0;
  EFI_STATUS Status;
  UINTN TmpAddress = Address;

  DEBUG((EFI_D_BLKIO, "NorFlashPlatformWrite: Address: 0x%x Len:0x%x\n", Address, BufferSizeInBytes));
  Remainder = BufferSizeInBytes % 256;
  Quotient = BufferSizeInBytes / 256;

  if(BufferSizeInBytes <= 256) {
    Status = SpiWrite(TmpAddress, Buffer, BufferSizeInBytes);
  } else {
    for(Index = 0; Index < Quotient; Index++) {
        Status = SpiWrite(TmpAddress, Buffer, 256);
        TmpAddress += 256;
        Buffer += 256;
    }

    if(Remainder != 0) {
      Status = SpiWrite(TmpAddress, Buffer, Remainder);
    }
  }

  if(EFI_ERROR(Status)) {
      DEBUG((EFI_D_ERROR, "%a() Line=%d\n", __FUNCTION__, __LINE__));
      ASSERT_EFI_ERROR(Status);
  }

  return EFI_SUCCESS;
}
//[jwluo-0015-start]
/*can write 256 bytes one time*/
static
EFI_STATUS
SpiWriteAtRunTime(
  IN UINTN            BaseAddr,
  IN UINTN            Address,
  IN VOID             *Buffer,
  IN UINT32           BufferSizeInBytes
  )
{
  UINT32 cmd_id = 0;
  UINT32 Index;
  UINT32 *TemBuffer = Buffer;
  EFI_STATUS Status;
  UINTN   FlashAddress = Address - BaseAddr;

  if(BufferSizeInBytes > 256) {
    DEBUG((EFI_D_ERROR, "Max Len is 256\n"));
    return EFI_INVALID_PARAMETER;
  }

  if(BufferSizeInBytes % 4 != 0) {
    DEBUG((EFI_D_ERROR, "Len must aligne 4 bytes\n"));
    return EFI_INVALID_PARAMETER;
  }

  if(Address % 4 != 0) {
    DEBUG((EFI_D_ERROR, "Address not aligne 4 byte \n"));
    return EFI_INVALID_PARAMETER;
  }

  cmd_id = mCmd_Pp;
  if(FlashAddress >= 0x1000000){
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24) | BIT12);
  }else{
    MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
  }
  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  cmd_id = mCmd_Write;
  if(FlashAddress >= 0x1000000){
    cmd_id = mCmd_Write16;
    MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x000208 | (cmd_id << 24) | BIT4);
  }else{
    MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x000208 | (cmd_id << 24));
  }

  for(Index = 0; Index < BufferSizeInBytes/4; Index++) {
    MmioWrite32(Address + Index * 4, TemBuffer[Index]);
  }

  Status = FlushFlash(Address, BufferSizeInBytes);
  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  MmioWrite32(mNorFlashControlBase + REG_FLUSH_REG, 0x1);

  asm volatile ("isb sy":::"cc");
  asm volatile ("dsb sy":::"cc");

  MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x0);

  return Status;
}


EFI_STATUS
NorFlashPlatformWriteAtRunTime (
  IN UINTN            BaseAddr,
  IN UINTN            Address,
  IN VOID             *Buffer,
  IN UINT32           BufferSizeInBytes
  )
{
  UINT32 Index = 0, Remainder = 0, Quotient = 0;
  EFI_STATUS Status;
  UINTN TmpAddress = Address;

  Remainder = BufferSizeInBytes % 256;
  Quotient = BufferSizeInBytes / 256;

  if(BufferSizeInBytes <= 256) {
    Status = SpiWriteAtRunTime(BaseAddr, TmpAddress, Buffer, BufferSizeInBytes);
  } else {
    for(Index = 0; Index < Quotient; Index++) {
        Status = SpiWriteAtRunTime(BaseAddr, TmpAddress, Buffer, 256);
        TmpAddress += 256;
        Buffer += 256;
    }

    if(Remainder != 0) {
      Status = SpiWriteAtRunTime(BaseAddr, TmpAddress, Buffer, Remainder);
    }
  }

  if(EFI_ERROR(Status)) {
      DEBUG((EFI_D_ERROR, "%a() Line=%d\n", __FUNCTION__, __LINE__));
      ASSERT_EFI_ERROR(Status);
  }

  return EFI_SUCCESS;
}
//[jwluo-0015-end]
